#include "scene.h"
#include <glm/gtx/vector_angle.hpp>

#define EPSILON 0.000004


Scene::Scene()
{
}


Scene::~Scene()
{
	clear();
}


void Scene::clear()
{
	Triangle::Iter i = _triangles.begin();
	for (; i != _triangles.end(); ++i) {
		delete *i;
	}
	_triangles.clear();
}


void Scene::load(const std::string& fname)
{
}


void Scene::save(const std::string& fname)
{
}

/*
bool Scene::closestIntersect(const Ray& ray, Intersection* is) const
{
	Triangle::cIter i = _triangles.begin();
	float t;

	is->distance = std::numeric_limits<float>::max();
	is->triangle = NULL;

	for (; i != _triangles.end(); ++i) {
		const Triangle* tr = *i;
		if (!tr->rayIntersect(ray, &t))
			continue;
		if (t < is->distance) {
			is->triangle = tr;
			is->distance = t;
		}
	}
	if (is->triangle) {
		is->point = ray.getStart() + is->distance * ray.getDirection();
		return true;
	}
	return false;
}*/


bool Scene::closestIntersect(const Ray& ray, Intersection* is) const
{
	const glm::vec3& dir = ray.getDirection();
	const glm::vec3& start = ray.getStart();
	// Matrix A (-dir, e1, e2)
	float a = -dir.x, b, c,
		  d = -dir.y, e, f,
		  g = -dir.z, h, i;
	// Vector b (start - triangle.v0)
	float j, k, l;
	// Seeking vector x
	float t, u, v;

	Triangle::cIter iter = _triangles.begin();
	is->distance = std::numeric_limits<float>::max();
	is->triangle = NULL;

	for (; iter != _triangles.end(); ++iter) {
		const Triangle* tr = *iter;
		const glm::vec3* tv = tr->getVertices();
		// Vector e1 (triangle.v1 - triangle.v0)
		b = tv[1].x - tv[0].x;
		e = tv[1].y - tv[0].y;
		h = tv[1].z - tv[0].z;
		// Vector e2 (triangle.v2 - triangle.v0)
		c = tv[2].x - tv[0].x;
		f = tv[2].y - tv[0].y;
		i = tv[2].z - tv[0].z;
		// Vector b (start - triangle.v0)
		j = start.x - tv[0].x;
		k = start.y - tv[0].y;
		l = start.z - tv[0].z;
		// Using Cramer's rule
		// Determinant of A
		float detA = a*(e*i-f*h) - b*(d*i-f*g) + c*(d*h-e*g);
		// Original conditions: u > 0, v > 0, u + v < 1, t >= 0, t < m.
		// If u = 0, v = 0 and (u + v) = 1 we are on the edge, we
		// include it as well to eliminate black edges. Also the
		// distance should be > 0.000001, which is an Epsilon
		// constant from Moller–Trumbore algorithm.
		u = (j*(f*g-i*d) + k*(a*i-g*c) + l*(c*d-f*a)) / detA;
		if (u < 0)
			continue;
		v = (j*(d*h-g*e) + k*(b*g-h*a) + l*(a*e-d*b)) / detA;
		if (v < 0 || u + v > 1)
			continue;
		t = (j*(e*i-h*f) + k*(c*h-i*b) + l*(b*f-e*c)) / detA;
		if (t < is->distance && t > EPSILON) {
			is->triangle = tr;
			is->distance = t;
		}
	}
	if (is->triangle) {
		is->point = start + is->distance * dir;
		return true;
	}
	return false;
}


void Scene::generateDefault()
{
	// Defines colors
	glm::vec3 red(0.75f, 0.15f, 0.15f);
	glm::vec3 yellow(0.75f, 0.75f, 0.15f);
	glm::vec3 green(0.15f, 0.75f, 0.15f);
	glm::vec3 cyan(0.15f, 0.75f, 0.75f);
	glm::vec3 blue(0.15f, 0.15f, 0.75f);
	glm::vec3 purple(0.75f, 0.15f, 0.75f);
	glm::vec3 white(0.75f, 0.75f, 0.75f);

	// Clear scene
	clear();

	//----------------------------------------------
	// Room
	//----------------------------------------------

	float L = 555;	// Length of Cornell Box side.

	glm::vec3 A(L, 0, 0);
	glm::vec3 B(0, 0, 0);
	glm::vec3 C(L, 0, L);
	glm::vec3 D(0, 0, L);

	glm::vec3 E(L, L, 0);
	glm::vec3 F(0, L, 0);
	glm::vec3 G(L, L, L);
	glm::vec3 H(0, L, L);

	// Floor
	_triangles.push_back(new Triangle(C, B, A, green));
	_triangles.push_back(new Triangle(C, D, B, green));

	// Left wall
	_triangles.push_back(new Triangle(A, E, C, purple));
	_triangles.push_back(new Triangle(C, E, G, purple));

	// Right wall
	_triangles.push_back(new Triangle(F, B, D, yellow));
	_triangles.push_back(new Triangle(H, F, D, yellow));

	// Ceiling
	_triangles.push_back(new Triangle(E, F, G, cyan));
	_triangles.push_back(new Triangle(F, H, G, cyan));

	// Back wall
	_triangles.push_back(new Triangle(G, D, C, white));
	_triangles.push_back(new Triangle(G, H, D, white));

	//----------------------------------------------
	// Short block
	//----------------------------------------------

	A = glm::vec3(290, 0, 114);
	B = glm::vec3(130, 0, 65);
	C = glm::vec3(240, 0, 272);
	D = glm::vec3(82, 0, 225);

	E = glm::vec3(290, 165, 114);
	F = glm::vec3(130, 165, 65);
	G = glm::vec3(240, 165, 272);
	H = glm::vec3(82, 165, 225);

	// Front
	_triangles.push_back(new Triangle(E, B, A, red));
	_triangles.push_back(new Triangle(E, F, B, red));

	// Front
	_triangles.push_back(new Triangle(F, D, B, red));
	_triangles.push_back(new Triangle(F, H, D, red));

	// Back
	_triangles.push_back(new Triangle(H, C, D, red));
	_triangles.push_back(new Triangle(H, G, C, red));

	// Left
	_triangles.push_back(new Triangle(G, E, C, red));
	_triangles.push_back(new Triangle(E, A, C, red));

	// Top
	_triangles.push_back(new Triangle(G, F, E, red));
	_triangles.push_back(new Triangle(G, H, F, red));

	//----------------------------------------------
	// Tall block
	//----------------------------------------------

	A = glm::vec3(423, 0, 247);
	B = glm::vec3(265, 0, 296);
	C = glm::vec3(472, 0, 406);
	D = glm::vec3(314, 0, 456);

	E = glm::vec3(423, 330, 247);
	F = glm::vec3(265, 330, 296);
	G = glm::vec3(472, 330, 406);
	H = glm::vec3(314, 330, 456);

	// Front
	_triangles.push_back(new Triangle(E, B, A, blue));
	_triangles.push_back(new Triangle(E, F, B, blue));

	// Front
	_triangles.push_back(new Triangle(F, D, B, blue));
	_triangles.push_back(new Triangle(F, H, D, blue));

	// Back
	_triangles.push_back(new Triangle(H, C, D, blue));
	_triangles.push_back(new Triangle(H, G, C, blue));

	// Left
	_triangles.push_back(new Triangle(G, E, C, blue));
	_triangles.push_back(new Triangle(E, A, C, blue));

	// Top
	_triangles.push_back(new Triangle(G, F, E, blue));
	_triangles.push_back(new Triangle(G, H, F, blue));

	//----------------------------------------------
	// Scale to the volume [-1, 1]^3
	//----------------------------------------------

	glm::vec3 mv(-1, -1, -1);
	Triangle::Iter i = _triangles.begin();
	for (; i != _triangles.end(); ++i) {
		Triangle* t = *i;

		t->scale(2.f / L);
		t->move(mv);
		t->mirrorX();
		t->mirrorY();
		t->computeNormal();
	}
}

